package com.xiyuan.smartutils;

import com.xiyuan.smartutils.constants.EnumConstants;

import java.util.regex.Matcher;

/**
 * IP地址相关工具类
 *
 * @version v1.0.0 @author lgz 2017-8-27 新建与整理
 */

public final class IPs implements EnumConstants {
    
    private IPs() {}
    
    /**
     *
     */
    public static final String IP_MIN_STR = "0.0.0.0";
    /**
     *
     */
    public static final String IP_MAX_STR = "255.255.255.255";
    /**
     *
     */
    public static final long IP_MIN = 0;                //ipToLong("0.0.0.0");
    public static final long IP_MAX = 4294967295L;      //ipToLong("255.255.255.255");
    
    /**
     * 4节字转字符串IP
     */
    private static String byteToIp(byte[] value) {
        StringBuilder strb = new StringBuilder();
        strb.append(value[0] & 0xFF).append(".");
        strb.append(value[1] & 0xFF).append(".");
        strb.append(value[2] & 0xFF).append(".");
        strb.append(value[3] & 0xFF);
        return strb.toString();
    }
    
    /**
     * 4节字转long型
     *
     * @return long ip
     */
    private static long byteToLong(byte[] value) {
        int i0 = value[0] & 0xFF;
        int i1 = value[1] & 0xFF;
        int i2 = value[2] & 0xFF;
        int i3 = value[3] & 0xFF;
        
        int ipInt = (i0 << 24) + (i1 << 16) + (i2 << 8) + i3;
        
        //转为无符号整数
        return ipInt & 0xFFFFFFFFL;
    }
    
    /**
     * 字符串IP转long型, use ipv4ToLong
     *
     * @param ip IP
     * @return long ip
     */
    public static long ipToLong(String ip) {
        return ipv4ToLong(ip);
    }
    
    /**
     * 根据ip地址(xxx.xxx.xxx.xxx)计算出long型的数据
     * 方法别名：inet_aton
     *
     * @param strIP IP V4 地址
     * @return long值
     */
    public static long ipv4ToLong(String strIP) {
        int[] ints = Arrays.toIntArray(strIP, "\\.");
        if (ints.length != 4) {return -1;}
        
        int ipInt = (ints[0] << 24) + (ints[1] << 16) + (ints[2] << 8) + (ints[3]);
        long ipLong = ipInt & 0x7FFFFFFFL;
        if (ipInt < 0) {ipLong |= 0x80000000L;}
        
        return ipLong;
    }
    
    /**
     * long型转IP格式
     *
     * @param ip IP
     * @return String Ip
     */
    public static String longToIp(long ip) {
        return longToIpv4(ip);
    }
    
    /**
     * 根据long值获取ip v4地址
     *
     * @param longIP IP的long表示形式
     * @return IP V4 地址
     * @see Ipv4Util#longToIpv4(long)
     */
    public static String longToIpv4(long longIP) {
        final StringBuilder sb = new StringBuilder(16);
        // 直接右移24位
        sb.append(longIP >> 24 & 0xFF);
        sb.append(".");
        // 将高8位置0，然后右移16位
        sb.append(longIP >> 16 & 0xFF);
        sb.append(".");
        sb.append(longIP >> 8 & 0xFF);
        sb.append(".");
        sb.append(longIP & 0xFF);
        return sb.toString();
    }
    
    /**
     * 把可能的255.255.003.002转化为255.255.3.2
     *
     * @param ip IP
     * @return String IP
     */
    public static String trim(String ip) {
        int[] values = Arrays.toIntArray(ip, "\\.");
        return Arrays.toString(values, ".");
    }
    
    /**
     * 子网掩码255.255.0.0格式转数字格式0-32，如果不是正确的掩码返回-1
     *
     * @param ipMask mask
     * @return int mask
     */
    public static int ipMaskToInt(String ipMask) {
        ipMask = trim(ipMask);
        for (int i = 0; i < _IP_MASK_.length; i++) {
            if (_IP_MASK_[i].equals(ipMask)) {return i;}
        }
        
        return -1;
    }
    
    /**
     * 子网掩码数字格式0-32格式转IP格式255.255.0.0
     *
     * @param i mask
     * @return String mask
     */
    public static String intToIpMask(int i) {
        if (i > 32) {return IP_MAX_STR;}
        
        if (i < 0) {return IP_MIN_STR;}
        
        return _IP_MASK_[i];
    }
    
    /**
     * 计算指定IP和掩码对应的允许的最大值和最小值IP
     *
     * @param ip   IP
     * @param mask 掩码
     * @return long[] {min,max}
     */
    public static long[] calcIpMaskMinMax(String ip, int mask) {
        long cur = ipToLong(ip);
        long min = IP_MIN;
        long max = IP_MAX;
        
        for (int i = 0; i <= 32; i++) {
            if (mask == i) {break;}
            
            //否则向下计算
            long half = (max - min - 1) / 2 + 1;
            if (cur >= (min + half)) {min += half;}
            else {max -= half;}
        }
        
        return new long[]{min, max};
    }
    
}
